import Math2D from "./Math2D";

// 二维向量
export default class Vector2 {

    public static zero = new Vector2(0, 0);
    public static xAxis = new Vector2(1, 0);    // x轴的正方向向量为[1,0]。
    public static yAxis = new Vector2(0, 1);    // y轴的正方向向量为[0,1]。
    public static nXAxis = new Vector2(- 1, 0); // x轴的负方向向量为[-1,0]。
    public static nYAxis = new Vector2(0, - 1); // y轴的负方向向量为[0,-1]。
    // public static temp = new Vector2(0, 0);
    // public static temp1 = new Vector2(0, 0);

    // 使用float32Array强类型数组，不需要进行引用类型到值类型，以及值类型到引用类型的转换，效率比较高
    public values: Float32Array;
    // 构造函数
    public constructor(x: number = 0, y: number = 0) {
        this.values = new Float32Array([x, y]);
    }

    public get x(): number { return this.values[0]; }
    public set x(x: number) { this.values[0] = x; }

    public get y(): number { return this.values[1]; }
    public set y(y: number) { this.values[1] = y; }

    // 为了重用向量，有时需要重置向量的x , y值
    public reset(x: number = 0, y: number = 0): Vector2 {
        this.values[0] = x;
        this.values[1] = y;
        return this;
    }
    // 静态的create方法
    public static create(x: number = 0, y: number = 0): Vector2 {
        return new Vector2(x, y);
    }
    // 复制当前的向量到result
    public static copy(src: Vector2, result: Vector2 | null = null): Vector2 {
        if (result === null) result = new Vector2();
        result.values[0] = src.values[0];
        result.values[1] = src.values[1];
        return result;
    }
    // 为了避免浮点数误差，使用EPSILON进行容差处理，默认情况下为0.00001
    public equals(vector: Vector2): boolean {
        if (Math.abs(this.values[0] - vector.values[0]) > Math2D.EPSILON)
            return false;
        if (Math.abs(this.values[1] - vector.values[1]) > Math2D.EPSILON)
            return false;
        return true;
    }
    // 当调用例如console . log方法时，会自动调用如下定义的toString方法
    public toString(): string {
        return " [ " + this.values[0] + " , " + this.values[1] + " ] ";
    }

    // 返回没有开根号的向量大小
    public get squaredLength(): number {
        let x = this.values[0];
        let y = this.values[1];
        return (x * x + y * y);
    }

    // 返回真正的向量大小
    public get length(): number {
        return Math.sqrt(this.squaredLength);
    }
    // 调用本方法后会在内部修改当前向量的x和y值，修改后的向量大小为1.0（单位向量或叫方向向量），并返回未修改前向量的大小
    public normalize(): number {
        // 计算出向量的大小
        let len: number = this.length;
        // 对0向量的判断与处理
        if (Math2D.isEquals(len, 0)) {
            console.log(" the length = 0 ");
            this.values[0] = 0;
            this.values[1] = 0;
            return 0;
        }
        // 如果已经是单位向量,直接返回1.0
        if (Math2D.isEquals(len, 1)) {
            console.log(" the length = 1 ");
            return 1.0;
        }
        // 否则计算出单位向量(也就是方向)
        this.values[0] /= len;
        this.values[1] /= len;
        // 同时返回向量的大小
        return len;
    }

    // 加
    public add(right: Vector2): Vector2 {
        // 不需要重新分配内存空间，效率相对较高
        Vector2.sum(this, right, this);
        return this;
    }

    // 向量求和
    public static sum(left: Vector2, right: Vector2, result: Vector2 | null = null): Vector2 {
        // 如果输出参数result为null，则分配内存给result变量
        if (result === null) result = new Vector2();
        // x和y分量分别相加，结果仍旧是一个向量
        result.values[0] = left.values[0] + right.values[0];
        result.values[1] = left.values[1] + right.values[1];
        // 返回相加后的向量result
        return result;
    }
    // 减
    public substract(another: Vector2): Vector2 {
        // 会修改this的x和y 分量
        // 不需要重新分配内存空间，效率相对较高
        Vector2.difference(this, another, this);
        return this;
    }

    // 向量求差
    public static difference(end: Vector2, start: Vector2, result: Vector2 | null = null): Vector2 {
        // 如果输出参数result为null,则分配 内存给result变量
        if (result === null) result = new Vector2();
        // x和y分量分别相减，结果仍旧是一个向量
        result.values[0] = end.values[0] - start.values[0];
        result.values[1] = end.values[1] - start.values[1];
        return result;
    }

    // 负向量
    public negative(): Vector2 {
        this.values[0] = - this.values[0];
        this.values[1] = - this.values[1];
        return this;
    }

    // 相向与标量相乘
    public static scale(direction: Vector2, scalar: number, result: Vector2 | null = null): Vector2 {
        if (result === null) result = new Vector2();
        result.values[0] = direction.values[0] * scalar;
        result.values[1] = direction.values[1] * scalar;
        return result;
    }

    public static scaleAdd(start: Vector2, direction: Vector2, scalar: number, result: Vector2 | null = null): Vector2 {
        if (result === null) result = new Vector2();
        Vector2.scale(direction, scalar, result);
        return Vector2.sum(start, result, result);
    }

    public static moveTowards(start: Vector2, direction: Vector2, scalar: number, result: Vector2 | null = null): Vector2 {
        if (result === null) result = new Vector2();
        Vector2.scale(direction, scalar, result);
        return Vector2.sum(start, result, result);
    }

    // 内积
    public innerProduct(right: Vector2): number {
        // 调用静态方法
        return Vector2.dotProduct(this, right);
    }

    // 点积
    public static dotProduct(left: Vector2, right: Vector2): number {
        // 两个向量的点乘就是对应分量的乘积的和，其返回的结果是一个标量
        return left.values[0] * right.values[0] + left.values[1] * right.values[1];
    }

    // 叉积，返回标量
    public static crossProduct(left: Vector2, right: Vector2): number {
        return left.x * right.y - left.y * right.x;
    }
    // 向量的朝向
    public static getOrientation(from: Vector2, to: Vector2, isRadian: boolean = false): number {
        let diff: Vector2 = Vector2.difference(to, from);
        let radian = Math.atan2(diff.y, diff.x);
        if (isRadian === false) {
            radian = Math2D.toDegree(radian);
        }
        return radian;
    }

    // 向量a和向量b的夹角
    public static getAngle(a: Vector2, b: Vector2, isRadian: boolean = false): number {
        let dot: number = Vector2.dotProduct(a, b);
        let radian: number = Math.acos(dot / (a.length * b.length));
        if (isRadian === false) {
            radian = Math2D.toDegree(radian);
        }
        return radian;
    }

    public static cosAngle(a: Vector2, b: Vector2, norm: boolean = false): number {
        if (norm === true) {
            a.normalize();
            b.normalize();
        }
        return Vector2.dotProduct(a, b);
    }

    public static sinAngle(a: Vector2, b: Vector2, norm: boolean = false): number {
        if (norm === true) {
            a.normalize();
            b.normalize();
        }
        return (a.x * b.y - b.x * a.y);
    }
}